/*
 * QTextDocumentAdapter.cpp  0.4.0 / Dec 04, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "QTextDocumentAdapter.h"

#include "Cute4jSwing.h"
#include "TextDocumentDelegate.h"

// TextDocumentHighlighter :: Public Constructor. ------------------------------

/*!
 * \brief Constructs new TextDocumentHighlighter object.
 */
TextDocumentHighlighter::TextDocumentHighlighter()
  : QSyntaxHighlighter((QObject *)0), m_document(0), m_blockPosition(0),
    m_contentsChanged(false)
{
  // empty constructor body
}

// TextDocumentHighlighter :: Public Member Functions. -------------------------

/*!
 * \brief Sets document.
 *
 * \param document the document
 */
void TextDocumentHighlighter::setDocument(QTextDocumentAdapter *document)
{
  m_document = document;
  connect(document, SIGNAL(contentsChange(int,int,int)), this,
          SLOT(contentsChangeSlot(int,int,int)));
  QSyntaxHighlighter::setDocument(document);
}

/*!
 * \brief Sets the format for the specified text segment.
 *
 * \param start the start of the segment
 * \param length the length of the segment
 * \param format the format
 */
void TextDocumentHighlighter::setFormat(int start, int length,
                                        const QTextCharFormat &format)
{
  QSyntaxHighlighter::setFormat(start, length, format);
}

// TextDocumentHighlighter :: Protected Member Function. -----------------------

/*!
 * \brief \c contentsChange slot.
 *
 * \param position the position
 * \param charsRemoved the number of removed characters
 * \param charsAdded the number of removed characters
 */
void TextDocumentHighlighter::contentsChangeSlot(int position,
                                                 int charsRemoved,
                                                 int charsAdded)
{
  Q_UNUSED(charsRemoved);
  Q_UNUSED(charsAdded);
  QTextBlock textBlock = m_document->findBlock(position);
  if (textBlock.isValid()) {
    m_blockPosition = textBlock.position();
  } else {
    m_blockPosition = 0;
  }
  m_contentsChanged = true;
}

// TextDocumentHighlighter :: Overridden Member Function. ----------------------

/*!
 * \inheritDoc
 */
void TextDocumentHighlighter::highlightBlock(const QString &text)
{
  if (!m_contentsChanged) {
    int previousState = previousBlockState();
    if (previousState >= 0) {
      QTextBlock textBlock = m_document->findBlock(previousState);
      m_blockPosition = textBlock.next().position();
    }
  }
  setCurrentBlockState(m_blockPosition);
  m_contentsChanged = false;
  m_document->highlightBlock(m_blockPosition, text);
}

// QTextDocumentAdapter :: Public Constructor. ---------------------------------

/*!
 * \brief Constructs new QLabelAdapter object.
 *
 * \param delegate the document delegate
 */
QTextDocumentAdapter::QTextDocumentAdapter(TextDocumentDelegate *delegate)
  : QTextDocument(), m_length(-1), m_destroying(false), m_delegate(delegate),
    m_highlighter(0)
{
  connect(this, SIGNAL(contentsChange(int, int, int)),
          SLOT(contentsChangeSlot(int, int, int)));
}

// QTextDocumentAdapter :: Public Destructor. ----------------------------------

/*!
 * \brief Destroys the QTextDocumentAdapter object.
 */
QTextDocumentAdapter::~QTextDocumentAdapter()
{
  if (m_highlighter) {
    delete m_highlighter;
  }
}

// QTextDocumentAdapter :: Public Member Functions. ----------------------------

/*!
 * \brief Returns length of the document.
 *
 * \return length of the document.
 */
int QTextDocumentAdapter::length()
{
  return m_length;
}

/*!
 * \brief Clears text.
 */
void QTextDocumentAdapter::clear()
{
  QTextDocument::clear();
  m_length = -1;
  // Qt bug workaround ?
  setUndoRedoEnabled(false);
  setUndoRedoEnabled(true);
  emit textCleared();
}

/*!
 * \brief Sets destroying state.
 */
void QTextDocumentAdapter::setDestroying()
{
  m_destroying = true;
  emit destroying();
}

/*!
 * \brief Sets whether or not this document is using the highlighter.
 *
 * \param useHighlighter \c true if using
 */
void QTextDocumentAdapter::setUseHighlighter(bool useHighlighter)
{
  if (m_destroying) {
    LOG_WARNING("Using disposed document");
    return;
  }
  if (m_highlighter) {
    delete m_highlighter;
    m_highlighter = 0;
  }
  if (useHighlighter) {
    m_highlighter = new TextDocumentHighlighter();
    m_highlighter->setDocument(this);
  }
}

/*!
 * \brief Sets the format for the specified text segment.
 *
 * \param start the start of the segment
 * \param length the length of the segment
 * \param format the format
 */
void QTextDocumentAdapter::setFormat(int start, int length,
                                     const QTextCharFormat &format)
{
  if (m_destroying) {
    LOG_WARNING("Using disposed document");
    return;
  }
  m_highlighter->setFormat(start, length, format);
}

/*!
 * \brief Highlights the text block of document.
 *
 * \param blockStart the block start
 * \param blockText the block text
 */
void QTextDocumentAdapter::highlightBlock(int blockStart,
                                          const QString &blockText)
{
  m_delegate->highlightBlock(blockStart, SwingString::toJString(blockText));
}

// QTextDocumentAdapter :: Protected Member Function. --------------------------

/*!
 * \brief \c contentsChange slot.
 *
 * \param position the position
 * \param charsRemoved the number of removed characters
 * \param charsAdded the number of added characters
 */
void QTextDocumentAdapter::contentsChangeSlot(int position, int charsRemoved,
                                              int charsAdded)
{
  Q_UNUSED(position);
  int oldLength = m_length;
  m_length -= charsRemoved;
  m_length += charsAdded;
  if ((m_length <= 0) || (oldLength <= 0)) {
    emit selectAllAvailable(!isEmpty());
  }
}
